package com.asr.testclient.service;

import com.asr.testclient.model.AudioInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Base64;

/**
 * 音频文件处理类
 * 负责文件选择、读取和Base64编码
 */
public class AudioFileHandler {
    private static final Logger logger = LoggerFactory.getLogger(AudioFileHandler.class);
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB

    /**
     * 打开文件选择对话框，过滤WAV和PCM文件
     * @param parent 父组件
     * @return 选择的文件，如果取消返回null
     */
    public File selectAudioFile(Component parent) {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setDialogTitle("选择音频文件");
        
        // 设置文件过滤器，只显示WAV和PCM文件
        FileNameExtensionFilter filter = new FileNameExtensionFilter(
            "音频文件 (*.wav, *.pcm)", "wav", "pcm", "WAV", "PCM"
        );
        fileChooser.setFileFilter(filter);
        fileChooser.setAcceptAllFileFilterUsed(false);

        int result = fileChooser.showOpenDialog(parent);
        
        if (result == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            logger.info("Selected file: {}", selectedFile.getAbsolutePath());
            return selectedFile;
        }
        
        logger.debug("File selection cancelled");
        return null;
    }

    /**
     * 使用缓冲流读取文件内容
     * @param file 要读取的文件
     * @return 文件内容的字节数组
     * @throws IOException 如果读取失败
     */
    public byte[] readAudioFile(File file) throws IOException {
        if (file == null) {
            logger.error("File is null");
            throw new IOException("文件对象为空");
        }

        if (!file.exists()) {
            logger.error("File does not exist: {}", file.getAbsolutePath());
            throw new IOException("文件不存在: " + file.getName());
        }

        if (!file.isFile()) {
            logger.error("Path is not a file: {}", file.getAbsolutePath());
            throw new IOException("路径不是一个文件: " + file.getName());
        }

        if (!file.canRead()) {
            logger.error("Cannot read file: {}", file.getAbsolutePath());
            throw new IOException("无法读取文件，请检查文件权限: " + file.getName());
        }

        long fileSize = file.length();
        if (fileSize == 0) {
            logger.warn("File is empty: {}", file.getName());
            throw new IOException("文件为空: " + file.getName());
        }

        logger.debug("Reading file: {}, size: {} bytes", file.getName(), fileSize);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
             ByteArrayOutputStream baos = new ByteArrayOutputStream((int) fileSize)) {
            
            byte[] buffer = new byte[8192];
            int bytesRead;
            long totalBytesRead = 0;
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
                totalBytesRead += bytesRead;
            }
            
            byte[] fileData = baos.toByteArray();
            logger.info("Successfully read {} bytes from file: {}", fileData.length, file.getName());
            
            if (fileData.length == 0) {
                logger.warn("Read 0 bytes from file: {}", file.getName());
                throw new IOException("文件读取结果为空: " + file.getName());
            }
            
            return fileData;
            
        } catch (FileNotFoundException e) {
            logger.error("File not found: {}", file.getAbsolutePath(), e);
            throw new IOException("文件未找到: " + file.getName(), e);
        } catch (SecurityException e) {
            logger.error("Security exception reading file: {}", file.getAbsolutePath(), e);
            throw new IOException("没有权限读取文件: " + file.getName(), e);
        } catch (IOException e) {
            logger.error("IO error reading file: {}", file.getAbsolutePath(), e);
            throw new IOException("读取文件时发生IO错误: " + e.getMessage(), e);
        }
    }

    /**
     * 将字节数组编码为Base64字符串
     * @param data 要编码的字节数组
     * @return Base64编码的字符串
     */
    public String encodeToBase64(byte[] data) {
        if (data == null) {
            logger.error("Cannot encode null data");
            return "";
        }

        if (data.length == 0) {
            logger.warn("Attempting to encode empty data");
            return "";
        }

        try {
            String encoded = Base64.getEncoder().encodeToString(data);
            logger.debug("Encoded {} bytes to Base64 string of length {}", data.length, encoded.length());
            
            if (encoded.isEmpty()) {
                logger.error("Base64 encoding resulted in empty string");
                return "";
            }
            
            return encoded;
        } catch (OutOfMemoryError e) {
            logger.error("Out of memory while encoding to Base64, data size: {} bytes", data.length, e);
            return "";
        } catch (Exception e) {
            logger.error("Failed to encode data to Base64, data size: {} bytes", data.length, e);
            return "";
        }
    }

    /**
     * 检查文件大小是否超过10MB限制
     * @param file 要检查的文件
     * @return true如果文件大小在限制内，false如果超过限制
     */
    public boolean validateFileSize(File file) {
        if (file == null || !file.exists()) {
            logger.warn("Cannot validate size of null or non-existent file");
            return false;
        }

        long fileSize = file.length();
        boolean isValid = fileSize <= MAX_FILE_SIZE;
        
        if (!isValid) {
            logger.warn("File size {} bytes exceeds maximum allowed size {} bytes", 
                       fileSize, MAX_FILE_SIZE);
        } else {
            logger.debug("File size {} bytes is within allowed limit", fileSize);
        }
        
        return isValid;
    }

    /**
     * 验证文件格式是否为WAV或PCM
     * @param file 要验证的文件
     * @return true如果文件格式有效，false如果格式无效
     */
    public boolean validateFileFormat(File file) {
        if (file == null || !file.exists()) {
            logger.warn("Cannot validate format of null or non-existent file");
            return false;
        }

        String fileName = file.getName().toLowerCase();
        boolean isValid = fileName.endsWith(".wav") || fileName.endsWith(".pcm");
        
        if (!isValid) {
            logger.warn("Invalid file format: {}. Only WAV and PCM files are supported", fileName);
        } else {
            logger.debug("File format is valid: {}", fileName);
        }
        
        return isValid;
    }

    /**
     * 生成WAV文件头
     * @param pcmDataSize PCM数据大小（字节）
     * @param sampleRate 采样率
     * @param bitDepth 位深度
     * @param channels 声道数
     * @return WAV文件头字节数组（44字节）
     */
    public byte[] generateWavHeader(int pcmDataSize, int sampleRate, int bitDepth, int channels) {
        byte[] header = new byte[44];
        ByteBuffer buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN);

        int byteRate = sampleRate * channels * bitDepth / 8;
        int blockAlign = channels * bitDepth / 8;

        // RIFF chunk descriptor
        buffer.put("RIFF".getBytes());                    // ChunkID (4 bytes)
        buffer.putInt(36 + pcmDataSize);                  // ChunkSize (4 bytes)
        buffer.put("WAVE".getBytes());                    // Format (4 bytes)

        // fmt sub-chunk
        buffer.put("fmt ".getBytes());                    // Subchunk1ID (4 bytes)
        buffer.putInt(16);                                // Subchunk1Size (4 bytes) - PCM
        buffer.putShort((short) 1);                       // AudioFormat (2 bytes) - PCM = 1
        buffer.putShort((short) channels);                // NumChannels (2 bytes)
        buffer.putInt(sampleRate);                        // SampleRate (4 bytes)
        buffer.putInt(byteRate);                          // ByteRate (4 bytes)
        buffer.putShort((short) blockAlign);              // BlockAlign (2 bytes)
        buffer.putShort((short) bitDepth);                // BitsPerSample (2 bytes)

        // data sub-chunk
        buffer.put("data".getBytes());                    // Subchunk2ID (4 bytes)
        buffer.putInt(pcmDataSize);                       // Subchunk2Size (4 bytes)

        logger.debug("Generated WAV header: {}Hz, {}bit, {} channels, {} bytes PCM data", 
                    sampleRate, bitDepth, channels, pcmDataSize);

        return header;
    }

    /**
     * 将PCM数据转换为完整的WAV文件数据
     * @param pcmData PCM音频数据
     * @param sampleRate 采样率
     * @param bitDepth 位深度
     * @param channels 声道数
     * @return 完整的WAV文件数据（包含文件头）
     */
    public byte[] createWavFile(byte[] pcmData, int sampleRate, int bitDepth, int channels) {
        if (pcmData == null || pcmData.length == 0) {
            logger.error("Cannot create WAV file from null or empty PCM data");
            return null;
        }

        try {
            byte[] header = generateWavHeader(pcmData.length, sampleRate, bitDepth, channels);
            byte[] wavFile = new byte[header.length + pcmData.length];
            
            System.arraycopy(header, 0, wavFile, 0, header.length);
            System.arraycopy(pcmData, 0, wavFile, header.length, pcmData.length);
            
            logger.info("Created WAV file: {} bytes (header: {}, data: {})", 
                       wavFile.length, header.length, pcmData.length);
            
            return wavFile;
        } catch (Exception e) {
            logger.error("Failed to create WAV file", e);
            return null;
        }
    }

    /**
     * 检测字节数组是否已经是WAV文件（通过检查RIFF头）
     * @param data 要检测的字节数组
     * @return true如果是WAV文件，false如果不是
     */
    public boolean isWavFile(byte[] data) {
        if (data == null || data.length < 12) {
            return false;
        }

        // 检查RIFF标识
        String riff = new String(data, 0, 4);
        if (!"RIFF".equals(riff)) {
            return false;
        }

        // 检查WAVE标识
        String wave = new String(data, 8, 4);
        return "WAVE".equals(wave);
    }

    /**
     * 检测字节数组是否已经包含音频文件头（WAV, WebM, MP3, OGG等）
     * @param data 要检测的字节数组
     * @return true如果已经是完整的音频文件，false如果是裸PCM数据
     */
    public boolean hasAudioFileHeader(byte[] data) {
        if (data == null || data.length < 4) {
            return false;
        }

        // 检查WAV (RIFF)
        if (data.length >= 12) {
            String riff = new String(data, 0, 4);
            if ("RIFF".equals(riff)) {
                String wave = new String(data, 8, 4);
                if ("WAVE".equals(wave)) {
                    logger.debug("Detected WAV file format");
                    return true;
                }
            }
        }

        // 检查WebM/Matroska (0x1A45DFA3)
        if (data.length >= 4) {
            if (data[0] == 0x1A && data[1] == 0x45 && (data[2] & 0xFF) == 0xDF && (data[3] & 0xFF) == 0xA3) {
                logger.debug("Detected WebM/Matroska file format");
                return true;
            }
        }

        // 检查MP3 (ID3 tag or MPEG sync)
        if (data.length >= 3) {
            // ID3v2 tag
            if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
                logger.debug("Detected MP3 file format (ID3)");
                return true;
            }
            // MPEG sync word (0xFFE or 0xFFF)
            if ((data[0] & 0xFF) == 0xFF && (data[1] & 0xE0) == 0xE0) {
                logger.debug("Detected MP3 file format (MPEG sync)");
                return true;
            }
        }

        // 检查OGG (OggS)
        if (data.length >= 4) {
            String ogg = new String(data, 0, 4);
            if ("OggS".equals(ogg)) {
                logger.debug("Detected OGG file format");
                return true;
            }
        }

        // 检查FLAC (fLaC)
        if (data.length >= 4) {
            String flac = new String(data, 0, 4);
            if ("fLaC".equals(flac)) {
                logger.debug("Detected FLAC file format");
                return true;
            }
        }

        logger.debug("No audio file header detected, assuming raw PCM data");
        return false;
    }

    /**
     * 从Base64字符串创建带WAV头的完整音频文件
     * 如果数据已经包含音频文件头，则直接返回；否则添加WAV头
     * @param base64Data Base64编码的音频数据
     * @param sampleRate 采样率
     * @param bitDepth 位深度
     * @return Base64编码的完整音频文件
     */
    public String createWavFromBase64(String base64Data, int sampleRate, int bitDepth) {
        if (base64Data == null || base64Data.trim().isEmpty()) {
            logger.error("Cannot create WAV from null or empty Base64 data");
            return null;
        }

        try {
            // 解码Base64数据
            byte[] audioData = Base64.getDecoder().decode(base64Data.trim());
            logger.info("Decoded Base64 data: {} bytes", audioData.length);

            // 检查是否已经包含音频文件头
            if (hasAudioFileHeader(audioData)) {
                logger.info("Data already contains audio file header, returning as-is");
                return base64Data.trim();
            }

            logger.info("Data is raw PCM, adding WAV header");

            // 默认单声道
            int channels = 1;

            // 创建完整的WAV文件
            byte[] wavFile = createWavFile(audioData, sampleRate, bitDepth, channels);
            
            if (wavFile == null) {
                logger.error("Failed to create WAV file");
                return null;
            }

            // 重新编码为Base64
            String wavBase64 = encodeToBase64(wavFile);
            logger.info("Created WAV file and encoded to Base64: {} bytes -> {} chars", 
                       wavFile.length, wavBase64.length());

            return wavBase64;
        } catch (IllegalArgumentException e) {
            logger.error("Invalid Base64 data", e);
            return null;
        } catch (Exception e) {
            logger.error("Unexpected error creating WAV from Base64", e);
            return null;
        }
    }

    /**
     * 解析WAV文件头，提取音频信息
     * @param file WAV文件
     * @return AudioInfo对象，如果解析失败返回null
     */
    public AudioInfo parseWavFileInfo(File file) {
        if (file == null || !file.exists()) {
            logger.warn("Cannot parse null or non-existent file");
            return null;
        }

        String fileName = file.getName().toLowerCase();
        
        // 如果是PCM文件，返回默认值
        if (fileName.endsWith(".pcm")) {
            logger.info("PCM file detected, using default audio info");
            return new AudioInfo(48000, 16, 1);
        }

        // 解析WAV文件头
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            byte[] header = new byte[44];
            int bytesRead = bis.read(header);
            
            if (bytesRead < 44) {
                logger.warn("WAV file header too short: {} bytes", bytesRead);
                return null;
            }

            // 验证RIFF标识
            String riff = new String(header, 0, 4);
            if (!"RIFF".equals(riff)) {
                logger.warn("Invalid WAV file: missing RIFF header");
                return null;
            }

            // 验证WAVE标识
            String wave = new String(header, 8, 4);
            if (!"WAVE".equals(wave)) {
                logger.warn("Invalid WAV file: missing WAVE header");
                return null;
            }

            // 读取音频格式信息（小端序）
            ByteBuffer buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN);
            
            // 跳过RIFF和WAVE标识，找到fmt chunk
            int channels = buffer.getShort(22) & 0xFFFF;
            int sampleRate = buffer.getInt(24);
            int bitDepth = buffer.getShort(34) & 0xFFFF;

            logger.info("Parsed WAV file info - Sample Rate: {}Hz, Bit Depth: {}bit, Channels: {}", 
                       sampleRate, bitDepth, channels);

            return new AudioInfo(sampleRate, bitDepth, channels);

        } catch (IOException e) {
            logger.error("Error parsing WAV file: {}", file.getName(), e);
            return null;
        } catch (Exception e) {
            logger.error("Unexpected error parsing WAV file: {}", file.getName(), e);
            return null;
        }
    }
}
